#ifndef LOCKER_H
#define LOCKER_H

#include <stdexcept>
#include <system_error>
#include <pthread.h>
#include <semaphore.h>

//sychronized locker
class Locker
{
    public:
        //obvious constructor "explicit"
        explicit Locker()
        {
            int ret = pthread_mutex_init(&m_mutex, NULL);
            if (ret != 0)
            {
                throw std::system_error(ret, std::system_category(), "locker init failed");
            }
        }

        ~Locker()
        {
            pthread_mutex_destroy(&m_mutex);
        }

        bool lock()
        {
            return pthread_mutex_lock(&m_mutex) == 0;
        }

        bool unlock()
        {
            return pthread_mutex_unlock(&m_mutex) == 0;
        }
    private:
        pthread_mutex_t m_mutex;
};

//semaphore mechanism
class Sem
{
    public:
        Sem()
        {
            int ret = sem_init(&m_sem, 0, 0);
            if (ret != 0)
            {
                throw std::system_error(ret, std::system_category(), "sem init failed");
            }
        }

        ~Sem()
        {
            sem_destroy(&m_sem);
        }

        bool wait()
        {
            return sem_wait(&m_sem) == 0;
        }

        bool post()
        {
            return sem_post(&m_sem) == 0;
        }
    private:
        sem_t m_sem;
};

/**
 * condition and mutex
 * 1. mostly condition is relevent to shared resource
 * 2. condition allows one thread to wait , until other thread notify it 
 */
class Cond
{
    public:
        Cond()
        {
            int ret = pthread_cond_init(&m_cond, NULL);
            if (ret != 0)
            {
                throw std::system_error(ret, std::system_category(), "cond init failed");
            }
        }

        ~Cond()
        {
            pthread_cond_destroy(&m_cond);
        }

        bool wait(pthread_mutex_t *mutex)
        {
            int ret = 0;
            //thread is waiting for condition to be true
            ret = pthread_cond_wait(&m_cond, mutex);
            return ret == 0;
        }

        bool timewait(pthread_mutex_t *m_mutex, struct timespec t)
        {
            int ret = 0;
            
            ret = pthread_cond_timedwait(&m_cond, m_mutex, &t);
            return ret == 0;
        }

        bool signal()
        {
            return pthread_cond_signal(&m_cond) == 0;
        }
        bool broadcast()
        {
            return pthread_cond_broadcast(&m_cond) == 0;
        }
    private:
        pthread_cond_t m_cond;
};
#endif // LOCKER_H